package polimi.reds.optimizer;

import java.io.Serializable;
import java.util.Collection;

import polimi.reds.Filter;
import polimi.reds.NodeDescriptor;
import polimi.reds.broker.overlay.Overlay;
import polimi.reds.broker.overlay.PacketListener;
import polimi.reds.broker.routing.Reconfigurator;
import polimi.reds.broker.routing.Router;
import polimi.reds.broker.routing.SubscriptionTable;

public class ReconfiguratorOptimizerDecorator implements Reconfigurator, PacketListener {
	
	private Reconfigurator reconf;
	private PacketListener listener;
	private TrafficLock trafficLock;
	private NodeDescriptor node=null;
	private SubscriptionTable table=null;
	private Overlay overlay=null;
	
	public ReconfiguratorOptimizerDecorator(Reconfigurator reconf, PacketListener listener, TrafficLock trafficLock) {
		this.reconf = reconf;
		this.trafficLock = trafficLock;
		this.listener = listener;
	}

	@Override
	public synchronized void setRouter(Router r) {
		reconf.setRouter(r);
		this.overlay = r.getOverlay();
		this.node = overlay.getNodeDescriptor();
		this.table = r.getSubscriptionTable();
	}

	@Override
	public void notifyNeighborAdded(NodeDescriptor addedNeighbor,
			Serializable reconfInfo) {
		
		synchronized(trafficLock) {
			if (trafficLock.isLocked()) {
				
				if (node.equals(trafficLock.getN1()) && addedNeighbor.equals(trafficLock.getN2())) {
					Collection<Filter> filters = table.getAllFilters(trafficLock.getN0());
					table.removeAllSubscriptions(trafficLock.getN0());
					for (Filter f:filters) {
						table.addSubscription(trafficLock.getN2(), f);
					}
					return;
				} else if (node.equals(trafficLock.getN2()) && addedNeighbor.equals(trafficLock.getN1())) {
					//Collection<Filter> filters = table.getAllFilters(trafficLock.getN0());
					//table.removeAllSubscriptions(trafficLock.getN0());
					//table.removeAllSubscriptions(trafficLock.getN1());
					//for (Filter f:filters) {
					//	table.addSubscription(trafficLock.getN1(), f);
					//}
					return;
				}
			}
		}
		reconf.notifyNeighborAdded(addedNeighbor, reconfInfo);
	}

	@Override
	public synchronized void notifyNeighborDead(NodeDescriptor deadNeighbor,
			Serializable reconfInfo) {
		reconf.notifyNeighborDead(deadNeighbor, reconfInfo);
	}

	@Override
	public synchronized void notifyNeighborRemoved(NodeDescriptor removedNeighbor) {
		
		synchronized(trafficLock) {
			if (trafficLock.isLocked()) {
				
				if (node.equals(trafficLock.getN0()) && removedNeighbor.equals(trafficLock.getN1())) {
					Collection<Filter> filters = table.getAllFilters(trafficLock.getN1());
					table.removeAllSubscriptions(trafficLock.getN1());
					for (Filter f:filters) {
						table.addSubscription(trafficLock.getN2(), f);
					}
					return;
				} else if (node.equals(trafficLock.getN1()) && removedNeighbor.equals(trafficLock.getN0())) {
					return;
				}
			}
		}
		reconf.notifyNeighborRemoved(removedNeighbor);
	}

	@Override
	public void notifyPacketArrived(String subject, NodeDescriptor source,
			Serializable packet) {
		listener.notifyPacketArrived(subject, source, packet);
	}
}
